﻿using System;
using System.Diagnostics;

namespace Simp.Model
{
	/// <summary>Element agregating multiple expressions by applying a list of parameter expressions to an expression.</summary>
	[ExpressionName("Composite")]
	[ParameterPolicy(0)]
	[DebuggerDisplay("{ExpressionHelper.Serialize(this)}")]
	public class CompositeExpression : IExpression, ISimplifiableExpression, IExpressionSerializer
	{
		private readonly IExpression _innerExpression;
		private readonly IExpression[] _parameterList;

		/// <summary>Construct the composite expression.</summary>
		/// <param name="expression"></param>
		/// <param name="parameterList"></param>
		public CompositeExpression(IExpression expression, params IExpression[] parameterList)
		{
			if (expression == null)
			{
				throw new ArgumentNullException("expression");
			}
			if (parameterList == null)
			{
				throw new ArgumentNullException("parameterList");
			}
			_innerExpression = expression;
			_parameterList = parameterList;
		}

		/// <summary>Returns the expression being on which the parameters will be passed on.</summary>
		public IExpression InnerExpression
		{
			get { return _innerExpression; }
		}

		/// <summary>Returns the parameter list.</summary>
		public IExpression[] ParameterList
		{
			get { return _parameterList; }
		}

		#region ISimplifiableExpression Members
		IExpression ISimplifiableExpression.Simplify(SimplificationMode simplificationMode, params IExpression[] parameterList)
		{
			if (ParameterList.Length == 0)
			{	//	No parameters, this function is basically a variable
				return ExpressionHelper.Simplify(false, _innerExpression, simplificationMode);
			}
			else
			{	//	Simplifies the parameters
				bool hasChanged = false;
				IExpression[] newParameters = new IExpression[_parameterList.Length];

				for (int i = 0; i != newParameters.Length; ++i)
				{
					var param = _parameterList[i];
					var newValue = ExpressionHelper.Simplify(true, param, simplificationMode);

					if (newValue != null)
					{
						newParameters[i] = newValue;
						hasChanged = true;
					}
					else
					{
						newParameters[i] = param;
					}
				}

				IExpression result = ExpressionHelper.Simplify(true, _innerExpression, simplificationMode, newParameters);

				if (result == null)
				{
					if (!hasChanged)
					{	//	Nothing changed
						return null;
					}
					else
					{	//	Just the parameters changed
						return new CompositeExpression(
							_innerExpression,
							newParameters);
					}
				}
				else
				{
					return result;
				}
			}
		}
		#endregion

		#region IExpressionSerializer Members
		void IExpressionSerializer.Serialize(AppendOnlyStringBuilder builder, params IExpression[] parameters)
		{
			if (builder == null)
			{
				throw new ArgumentNullException("builder");
			}
			if (parameters == null)
			{
				throw new ArgumentNullException("parameters");
			}

			IExpressionSerializer expressionSerializer = _innerExpression as IExpressionSerializer;

			if (expressionSerializer == null)
			{
				ExpressionHelper.SerializeGeneric(builder, _innerExpression, _parameterList);
			}
			else
			{
				expressionSerializer.Serialize(builder, _parameterList);
			}
		}
		#endregion
	}
}